/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.rpc.registry.storage.database;

import io.iec.edp.caf.commons.transaction.JpaTransaction;
import io.iec.edp.caf.commons.transaction.TransactionPropagation;
import io.iec.edp.caf.rpc.api.entity.RpcServiceDefinition;
import io.iec.edp.caf.rpc.api.entity.RpcServiceMethodDefinition;
import io.iec.edp.caf.rpc.registry.storage.RpcDefinitionStorage;
import io.iec.edp.caf.rpc.registry.storage.database.domain.entity.RpcServiceDefinitionEntity;
import io.iec.edp.caf.rpc.registry.storage.database.domain.entity.RpcServiceMethodDefinitionEntity;
import io.iec.edp.caf.rpc.registry.storage.database.domain.manager.RpcDefinitionManager;
import io.iec.edp.caf.tenancy.api.context.MultiTenantContextInfo;
import io.iec.edp.caf.tenancy.core.context.MultiTenantContextHolder;
import org.springframework.beans.BeanUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Leon Huo
 */
public class RpcDefinitionStorageDatabaseImpl implements RpcDefinitionStorage {

    private RpcDefinitionManager manager;

    public RpcDefinitionStorageDatabaseImpl(RpcDefinitionManager manager) {
        this.manager = manager;
    }

    @Override
    public Boolean saveRpcDefinition(List<RpcServiceDefinition> definitionList) {
        List<RpcServiceDefinitionEntity> serviceEntityList = new ArrayList<>();
        List<RpcServiceMethodDefinitionEntity> serviceMethodEntityList = new ArrayList<>();

        for (RpcServiceDefinition rpcServiceDefinition : definitionList) {
            RpcServiceDefinitionEntity rpcServiceEntity = new RpcServiceDefinitionEntity();
            BeanUtils.copyProperties(rpcServiceDefinition, rpcServiceEntity);
            serviceEntityList.add(rpcServiceEntity);

            List<RpcServiceMethodDefinition> serviceMethodList = rpcServiceDefinition.getMethods();
            for (RpcServiceMethodDefinition serviceMethodDefinition : serviceMethodList) {
                RpcServiceMethodDefinitionEntity rpcServiceMethodEntity = new RpcServiceMethodDefinitionEntity();
                BeanUtils.copyProperties(serviceMethodDefinition, rpcServiceMethodEntity);
                rpcServiceMethodEntity.setServiceDefinitionId(rpcServiceDefinition.getId());
                serviceMethodEntityList.add(rpcServiceMethodEntity);
            }
        }

        //RPC信息注册至主库
        try {
            MultiTenantContextInfo contextInfo = new MultiTenantContextInfo();
            contextInfo.setMasterDb(true);
            MultiTenantContextHolder.set(contextInfo);

            JpaTransaction transaction = JpaTransaction.getTransaction();
            try {
                transaction.begin(TransactionPropagation.REQUIRES_NEW);

                this.manager.saveRpcServiceDefinition(serviceEntityList);
                this.manager.saveRpcServiceMethodDefinition(serviceMethodEntityList);

                transaction.commit();
            } catch (Throwable e) {
                transaction.rollback();
                throw new RuntimeException("RPC register errors", e);
            }
        } finally {
            MultiTenantContextHolder.set(null);
        }

        return true;
    }

    @Override
    public RpcServiceDefinition getRpcDefinition(String rpcServiceId) {
        RpcServiceDefinitionEntity rpcServiceDefinitionEntity = this.manager.getRpcServiceDefinition(rpcServiceId);
        RpcServiceDefinition rpcServiceDefinition = new RpcServiceDefinition();
        BeanUtils.copyProperties(rpcServiceDefinitionEntity, rpcServiceDefinition);

        List<RpcServiceMethodDefinitionEntity> rpcServiceMethodDefinitionEntityList = this.manager.getRpcServiceMethodDefinitions(rpcServiceId);
        List<RpcServiceMethodDefinition> serviceMethodDefinitionList = new ArrayList<>();
        for (RpcServiceMethodDefinitionEntity serviceMethodDefinitionEntity : rpcServiceMethodDefinitionEntityList) {
            RpcServiceMethodDefinition rpcServiceMethodDefinition = new RpcServiceMethodDefinition();
            BeanUtils.copyProperties(serviceMethodDefinitionEntity, rpcServiceMethodDefinition);
            rpcServiceMethodDefinition.setParentDefinition(rpcServiceDefinition);
            serviceMethodDefinitionList.add(rpcServiceMethodDefinition);
        }

        rpcServiceDefinition.setMethods(serviceMethodDefinitionList);
        return rpcServiceDefinition;
    }

    @Override
    public Boolean deleteRpcDefinition(List<RpcServiceDefinition> definitionList) {
        throw new RuntimeException("not support this method yet");
    }

    @Override
    public List<String> queryDefinitionList() {
        return manager.getRpcServiceDefinitionList();
    }

    @Override
    public List<RpcServiceDefinition> queryDefinitionListBySuName(String suName) {
        List<RpcServiceDefinitionEntity> rpcServiceDefinitionEntityList = manager.getRpcServiceDefinitionEntitiesBySu(suName);
        List<RpcServiceDefinition> result = new ArrayList<>();
        Map<String, RpcServiceDefinition> definitionMap = new HashMap<>();
        List<String> serviceIdList = new ArrayList<>();

        for (RpcServiceDefinitionEntity entity : rpcServiceDefinitionEntityList) {
            RpcServiceDefinition rpcServiceDefinition = new RpcServiceDefinition();
            BeanUtils.copyProperties(entity, rpcServiceDefinition);
            definitionMap.put(rpcServiceDefinition.getId(), rpcServiceDefinition);
            result.add(rpcServiceDefinition);
            serviceIdList.add(rpcServiceDefinition.getId());
        }

        List<RpcServiceMethodDefinitionEntity> rpcServiceMethodDefinitionEntityList = manager.getRpcServiceMethodDefinitionEntitiesByServiceDefinitionIdIn(serviceIdList);

        for (RpcServiceMethodDefinitionEntity entity : rpcServiceMethodDefinitionEntityList) {
            RpcServiceMethodDefinition rpcServiceMethodDefinition = new RpcServiceMethodDefinition();
            BeanUtils.copyProperties(entity, rpcServiceMethodDefinition);
            RpcServiceDefinition rpcServiceDefinition = definitionMap.get(entity.getServiceDefinitionId());
            rpcServiceMethodDefinition.setParentDefinition(rpcServiceDefinition);
            if (rpcServiceDefinition.getMethods() == null) {
                rpcServiceDefinition.setMethods(new ArrayList<>());
            }

            rpcServiceDefinition.getMethods().add(rpcServiceMethodDefinition);
        }

        return result;
    }
}
